﻿.net project start alternatives:
- ACTION: run course tracker
  VALUE: course tracker
- ACTION: run course tracker server
  VALUE: course tracker server
add:
  RESULT:
  - result
  STEPS:
  - EXEC: result = value1 + value2
  TAKES:
  - value1
  - value2
add addition:
  STEPS:
  - EXEC: |
      data['additions'].append({'date': '', 'reason': ''})
  TAKES:
  - data
  USES:
  - addition definition
  - course definition
add cancellation:
  STEPS:
  - EXEC: |
      data['cancellations'].append({'start date': '', 'end date': '', 'reason': ''})
  TAKES:
  - data
  USES:
  - cancellation definition
  - course definition
add course tracker course: null
add new course:
  STEPS:
  - create data from definition:
      RESULT:
      - AS: course data
        PARAM: data
      TAKES:
      - AS: definition
        PARAM: course definition
  - get value:
      RESULT:
      - AS: app context
        WHAT: value
      TAKES:
      - AS: dictionary
        WHAT: context
      - AS: key
        WHAT: app context
  - open view:
      TAKES:
      - AS: context
        PARAM: app context
      - AS: data
        WHAT: course data
      - view
      - AS: title
        LITERAL: Add New Course
      - AS: parent context
        PARAM: context
  TAKES:
  - context
  - AS: course definition
    KNOWLEDGE: course definition
  - AS: view
    KNOWLEDGE: course details view
addition definition: null
append items to list:
  STEPS:
  - EXEC: list.extend(items)
  TAKES:
  - list
  - items
append to list:
  STEPS:
  - EXEC: list.append(value)
  TAKES:
  - list
  - value
apply change list:
  RESULT:
  - data
  STEPS:
  - EXEC: |
      for changes in change_list:
        data = execute_knowledge_piece('apply changes inplace', {'data': data, 'changes': changes})['data']
    RESULT:
    - data
    TAKES:
    - data
    - change_list: change list
  TAKES:
  - data
  - change list
  USES:
  - apply changes
apply changes inplace:
  EXAMPLE:
    changes:
    - added: true
      d: d2
      id: 3
    - cc:
      - added: true
        d ref id: 3
        id: 3
      id: 2
    data:
    - a: a
      id: 1
    - b: b
      cc:
      - ca: ca
        id: 1
      - cb: cb
        id: 2
      id: 2
    - d: d1
      id: 3
    result data:
    - a: a
      id: 1
    - b: b
      cc:
      - ca: ca
        id: 1
      - cb: cb
        id: 2
      - d ref id: 4
        id: 3
      id: 2
    - d: d1
      id: 3
    - d: d2
      id: 4
  RESULT:
  - data
  STEPS:
  - EXEC: |
      if changes is None:
        pass
      elif type(changes) is dict:
        if type(data) is not dict:
          raise Exception("The type of 'changes' object should be the same as of the data")
        else:
          for key, item_changes in changes.iteritems():
            if item_changes is None:
              data.pop(key, None)
            else:
              value = data.get(key, None)
              if value is None:
                data[key] = item_changes
              else:
                new_value = execute_knowledge_piece('apply changes inplace', {'data': value, 'changes': item_changes})['data']
                data[key] = new_value
      elif type(changes) is list:
        if type(data) is not list:
          raise Exception("The type of 'changes' object should be the same as of the data")
        else:
          changes_left = list(changes)
          item_index = 0
          for item in list(data):
            if type(item) is dict and 'id' in item:
              id = item['id']
              index = 0
              deleted = False
              for item_changes in list(changes_left):
                if type(item_changes) is not dict or 'id' not in item_changes or item_changes['id'] != id:
                  index = index + 1
                  continue
                del changes_left[index]
                if item_changes.get('deleted'):
                  deleted = True
                  continue
                if deleted:
                  data[item_index] = item_changes
                else:
                  data[item_index] = execute_knowledge_piece('apply changes inplace', {'data': item, 'changes': item_changes})['data']
                deleted = False
              if deleted:
                del data[item_index]
              else:
                item_index = item_index + 1
            else:
              index = 0
              found = False
              for item_changes in list(changes_left):
                if type(item_changes) is dict and 'id' in item_changes:
                  index = index + 1
                  continue
                del changes_left[index]
                data[item_index] = item_changes
                found = True
                break
              if not found:
                del data[item_index]
              else:
                item_index = item_index + 1
          for item in changes_left:
            data.append(item)
      else:
        data = changes
  TAKES:
  - data
  - changes
apply common attributes to wpf element:
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import HorizontalAlignment
      from System.Windows import VerticalAlignment
      from System.Windows.Controls import Dock
      from System.Windows.Controls import DockPanel

      if 'horizontal alignment' in attributes:
        h_align = attributes.get('horizontal alignment')
        if h_align == 'left':
          element.HorizontalAlignment = HorizontalAlignment.Left
        elif h_align == 'center':
          element.HorizontalAlignment = HorizontalAlignment.Center
        elif h_align == 'right':
          element.HorizontalAlignment = HorizontalAlignment.Right
      if 'vertical alignment' in attributes:
        v_align = attributes.get('vertical alignment')
        if v_align == 'left':
          element.VerticalAlignment = VerticalAlignment.Left
        elif v_align == 'center':
          element.VerticalAlignment = VerticalAlignment.Center
        elif v_align == 'right':
          element.VerticalAlignment = VerticalAlignment.Right
      if 'dock' in attributes:
        dock_name = attributes.get('dock')
        dock = Dock.Top
        if dock_name == 'bottom':
          dock = Dock.Bottom
        if dock_name == 'left':
          dock = Dock.Left
        if dock_name == 'right':
          dock = Dock.Right
        DockPanel.SetDock(element, dock)
      if 'font size' in attributes:
        element.FontSize = attributes.get('font size')
      if 'width' in attributes:
        element.Width = attributes.get('width')
      if 'height' in attributes:
        element.Height = attributes.get('height')
    TAKES:
    - AS: element
      PARAM: wpf element
    - attributes
  TAKES:
  - wpf element
  - attributes
calculate course schedule data:
  RESULT:
  - data
  STEPS:
  - EXEC: |
      start_date = data['start date']
      num_lessons = data['number of lessons']
      result = execute_knowledge_piece('calculate schedule dates', {
        'start_date': start_date,
        'count': num_lessons,
        'schedule': data['schedule'],
        'holidays': holidays,
        'cancellations': data['cancellations'],
        'additions': data['additions'],
        })
      dates = result['dates']
      all_dates = result['all schedule dates']
      finish_date = dates[num_lessons - 1]
      test_lessons = data['test lessons']
      for date in all_dates:
        reason = date['reason'] or []
        if type(reason) is not list:
          reason = [reason]
        lesson = date['lesson #']
        if lesson is not None:
          if lesson == 1:
            reason.append('Start')
          for test_num in range(len(test_lessons)):
            test_lesson = test_lessons[test_num]
            if lesson == test_lesson:
              reason.append('Test %d' % (test_num + 1))
          if lesson == num_lessons:
            reason.append('Finish')
        if date['status'] == 'removed':
          if date['is holiday']:
            date['lesson #'] = 'Holiday'
          else:
            date['lesson #'] = 'Cancelled'
        if len(reason) > 0:
          date['reason'] = ', '.join(reason)
      data['holidays'] = [holiday for holiday in holidays if start_date <= holiday['start date'] <= finish_date or start_date <= holiday['end date'] <= finish_date]
      data['lesson dates'] = [{'lesson #': lesson + 1, 'date': dates[lesson]} for lesson in range(num_lessons)]
      data['all schedule dates'] = all_dates
      data['test dates'] = [(dates[lesson - 1] if 0 < lesson <= num_lessons else None) for lesson in test_lessons]
      data['finish date'] = finish_date
  TAKES:
  - data
  - AS: holidays
    KNOWLEDGE: russian holidays
  USES:
  - calculate schedule dates
  - course definition
calculate schedule dates:
  RESULT:
  - dates
  - all schedule dates
  STEPS:
  - EXEC: |
      from datetime import date
      #cancellations = holidays + cancellations
      removed_dates = dict()
      holiday_dates = set()
      for cancellations_collection in [holidays, cancellations]:
        for cancellation in cancellations_collection:
          start = cancellation['start date']
          end = cancellation['end date']
          reason = cancellation['reason']
          cur_date = start
          while cur_date <= end:
            removed_dates[cur_date] = removed_dates.get(cur_date, []) + [reason]
            if cancellations_collection is holidays:
              holiday_dates.add(cur_date)
            cur_date = date.fromordinal(cur_date.toordinal() + 1)
      added_dates = dict((addition['date'], addition['reason']) for addition in additions)
      dates = []
      all_dates = []
      current_date = start_date
      #write_to_log('locals: %s', repr(locals()))
      date_number = 0
      while date_number < count:
        week_day = current_date.weekday()
        #write_to_log('date_number: %d, week_day: %d', (date_number, week_day))
        status = None
        reason = None
        if current_date in added_dates:
          status = 'added'
          reason = added_dates[current_date]
        elif week_day in schedule:
          if current_date in removed_dates:
            status = 'removed'
            reason = removed_dates[current_date]
          else:
            status = 'by schedule'
        if status is not None:
          all_dates.append({'lesson #': (date_number + 1 if status != 'removed' else None), 'date': current_date, 'status': status, 'reason': reason, 'is holiday': current_date in holiday_dates})
          if status != 'removed':
            dates.append(current_date)
            date_number = date_number + 1
        current_date = date.fromordinal(current_date.toordinal() + 1)
    RESULT:
    - dates
    - AS: all schedule dates
      PARAM: all_dates
  TAKES:
  - start_date
  - count
  - schedule
  - holidays
  - cancellations
  - additions
cancel selected lessons:
  STEPS:
  - EXEC: |
      start_date = None
      end_date = None
      prev_lesson_num = None
      def add_cancellation(data, start_date, end_date):
        if start_date and end_date:
          data['cancellations'].append({'start date': start_date, 'end date': end_date, 'reason': ''})
      for lesson in sorted(selected_items, key = lambda item: item.get('lesson #')):
        if type(lesson) is dict:
          lesson_num = lesson.get('lesson #')
          lesson_date = lesson.get('date')
          if lesson_num is not None and lesson_date:
            if prev_lesson_num and lesson_num > prev_lesson_num + 1:
              add_cancellation(data, start_date, end_date)
              start_date = None
              end_date = None
            if not start_date or lesson_date < start_date:
              start_date = lesson_date
            if not end_date or lesson_date > end_date:
              end_date = lesson_date
            prev_lesson_num = lesson_num
      add_cancellation(data, start_date, end_date)
    TAKES:
    - AS: selected_items
      PARAM: selected items
    - data
  TAKES:
  - data
  - selected items
  USES:
  - cancellation definition
cancellation definition:
  end date: 2010-01-01
  reason: ''
  start date: 2010-01-01
choose server: null
choose storage path:
  STEPS:
  - get value:
      RESULT:
      - AS: app context
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: view context
      - AS: key
        LITERAL: app context
  - get course tracker data storage path:
      RESULT:
      - storage path
      TAKES:
      - AS: context
        PARAM: app context
  - show open file dialog:
      RESULT:
      - AS: storage path
        PARAM: path
      - is ok
      TAKES:
      - AS: initial path
        PARAM: storage path
  - make data:
      RESULT:
      - AS: alt context
        PARAM: data
      TAKES:
      - AS: template
        LITERAL:
          storage path: param2
          view context: param1
      - AS: param1
        PARAM: view context
      - AS: param2
        PARAM: storage path
      - AS: param3
        LITERAL: null
  - do alternatively:
      TAKES:
      - AS: context
        PARAM: alt context
      - AS: alternative_value
        PARAM: is ok
      - AS: alternative_actions
        LITERAL:
        - ACTION: set course tracker storage path
          VALUE: true
  TAKES:
  - AS: view context
    PARAM: context
  USES:
  - set course tracker storage path
clone data:
  RESULT:
  - data
  STEPS:
  - convert to yaml:
      RESULT:
      - yaml
      TAKES:
      - AS: kpiece
        PARAM: data
  - load yaml in python:
      RESULT:
      - AS: data
        PARAM: result
      TAKES:
      - AS: yaml_content
        PARAM: yaml
  TAKES:
  - data
convert date to date with weekday:
  RESULT:
  - converted_value
  STEPS:
  - EXEC: |
      from datetime import date
      if type(value) is not date:
        converted_value = ''
      else:
        weekday = value.weekday()
        converted_value = '%s, %s' % (value, weekdays[weekday])
  TAKES:
  - value
  - AS: weekdays
    WHAT: weekday short names
convert schedule to string:
  RESULT:
  - converted_value
  STEPS:
  - EXEC: |
      schedule = value
      schedule_string = ', '.join([weekdays[i] for i in sorted(schedule)])
      converted_value = schedule_string
  TAKES:
  - value
  - AS: weekdays
    WHAT: weekday short names
convert string to date:
  RESULT:
  - converted_value
  STEPS:
  - EXEC: |
      from System import DateTime
      from datetime import date

      if not value:
        converted_value = None
      date_time = DateTime.Parse(value)
      converted_value = date(date_time.Year, date_time.Month, date_time.Day)
  TAKES:
  - value
convert string to datetime:
  RESULT:
  - converted_value
  STEPS:
  - EXEC: |
      from System import DateTime
      from datetime import datetime

      if not value:
        converted_value = None
      else:
        date_time = DateTime.Parse(value)
        millis = date_time.Subtract(DateTime(date_time.Year, date_time.Month, date_time.Day, date_time.Hour, date_time.Minute, date_time.Second)).TotalMilliseconds
        micros = int(millis * 1000)
        converted_value = datetime(date_time.Year, date_time.Month, date_time.Day, date_time.Hour, date_time.Minute, date_time.Second, micros)
  TAKES:
  - value
convert string to schedule:
  RESULT:
  - converted_value
  STEPS:
  - EXEC: |
      parts = [part.lower() for part in value.replace(',', ' ').split()]
      converted_value = [i for i in range(7) if weekdays[i] in parts]
  TAKES:
  - value
  - AS: weekdays
    WHAT: weekday short names
convert to yaml:
  RESULT:
  - yaml
  STEPS:
  - EXEC: |-
      def to_yaml(kpiece):
        import yaml
        def represent_str(Dumper, data):
          style = '|' if ((type(data) is unicode and u'\n' in data) or (type(data) is str and '\n' in data)) else None
          return Dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style=style)
        yaml.add_representer(str, represent_str)
        yaml.add_representer(unicode, represent_str)
        class MyStream(object):
          def __init__(self):
            self.buf = []
          def write(self, data):
            self.buf.append(data)
          def getvalue(self):
            return ''.join(self.buf)
        stream = MyStream()
        yaml.dump(kpiece, stream = stream, default_style = None,
          default_flow_style=False, allow_unicode=True, encoding=None)
        return stream.getvalue()
      yaml = to_yaml(kpiece)
  TAKES:
  - kpiece
course definition:
  additions: []
  cancellations: []
  company: ''
  course name: ''
  finish date: null
  group or student: ''
  holidays: []
  id: 0
  number of lessons: 10
  schedule:
  - 0
  - 3
  start date: 2010-10-14
  test dates:
  - null
  - null
  - null
  - null
  test lessons:
  - 1
  - 2
  - 4
  - 8
course details view:
  USES:
  - view definition
  - schedule to string converter
  - calculate course schedule data
  - save course data
  - date to date with weekday converter
  - add cancellation
  - delete cancellation
  - add addition
  - delete addition
  - cancel selected lessons
  attributes:
    height: 550
    width: 700
  kind: dock panel
  links:
    children:
    - attributes:
        dock: top
        horizontal alignment: center
        vertical alignment: center
      kind: wrap panel
      links:
        children:
        - attributes:
            binding: company
            header: Company
          kind: data field
        - attributes:
            binding: group or student
            header: Group / Student
          kind: data field
        - attributes:
            binding: course name
            header: Course
          kind: data field
        - attributes:
            binding: schedule
            data converter: schedule to string converter
            header: Schedule
          kind: data field
        - attributes:
            binding: start date
            data type: date
            header: Start Date
          kind: data field
        - attributes:
            binding: number of lessons
            data type: integer
            header: '# Lessons'
          kind: data field
        - attributes:
            binding: last update time
            data type: datetime
            header: Last Update Time
            read only: true
          kind: data field
    - attributes:
        dock: top
        horizontal alignment: center
      kind: wrap panel
      links:
        children:
        - attributes: {}
          kind: dock panel
          links:
            children:
            - attributes:
                binding: test lessons
                binding index: 0
                data type: integer
                dock: top
                header: Test 1
              kind: data field
            - attributes:
                binding: test dates
                binding index: 0
                data type: date
                header: Test 1 Date
                read only: true
              kind: data field
        - attributes: {}
          kind: dock panel
          links:
            children:
            - attributes:
                binding: test lessons
                binding index: 1
                data type: integer
                dock: top
                header: Test 2
              kind: data field
            - attributes:
                binding: test dates
                binding index: 1
                data type: date
                header: Test 2 Date
                read only: true
              kind: data field
        - attributes: {}
          kind: dock panel
          links:
            children:
            - attributes:
                binding: test lessons
                binding index: 2
                data type: integer
                dock: top
                header: Test 3
              kind: data field
            - attributes:
                binding: test dates
                binding index: 2
                data type: date
                header: Test 3 Date
                read only: true
              kind: data field
        - attributes: {}
          kind: dock panel
          links:
            children:
            - attributes:
                binding: test lessons
                binding index: 3
                data type: integer
                dock: top
                header: Test 4
              kind: data field
            - attributes:
                binding: test dates
                binding index: 3
                data type: date
                header: Test 4 Date
                read only: true
              kind: data field
        - attributes: {}
          kind: dock panel
          links:
            children:
            - attributes:
                binding: number of lessons
                data type: integer
                dock: top
                header: Finish
              kind: data field
            - attributes:
                binding: finish date
                data type: date
                header: Finish Date
                read only: true
              kind: data field
    - attributes:
        command: calculate course schedule data
        dock: top
        name: Calculate Dates / Refresh
      kind: command
    - attributes:
        command: save course data
        dock: top
        name: Save
        type: submit
      kind: command
    - attributes:
        binding: all schedule dates
        dock: left
        header: Lesson Dates
        horizontal alignment: center
        read only: true
        ref name: lesson dates
        row style:
          font color:
            binding: status
            data converter: status to color converter
      kind: list
      links:
        columns:
        - attributes:
            binding: 'lesson #'
            data type: integer
            header: 'Lesson #'
          kind: column
        - attributes:
            binding: date
            data converter: date to date with weekday converter
            data type: date
            header: Lesson Date
          kind: column
        - attributes:
            binding: reason
            header: Reason
          kind: column
    - attributes:
        dock: right
      kind: vertical grid
      links:
        children:
        - attributes:
            binding: holidays
            header: Holidays
            read only: true
          kind: list
          links:
            columns:
            - attributes:
                binding: start date
                data type: date
                header: Start Date
              kind: column
            - attributes:
                binding: end date
                data type: date
                header: End Date
              kind: column
            - attributes:
                binding: reason
                header: Reason
              kind: column
        - attributes:
            binding: cancellations
            header: Cancellations/Breaks
            ref name: cancellations
          kind: list
          links:
            columns:
            - attributes:
                binding: start date
                data type: date
                header: Start Date
              kind: column
            - attributes:
                binding: end date
                data type: date
                header: End Date
              kind: column
            - attributes:
                binding: reason
                header: Reason
              kind: column
            commands:
            - attributes:
                command: cancel selected lessons
                name: Add Cancellation/Break
                name2: Cancel Selected Lesson(s)
                on selection empty:
                  command: add cancellation
                  name: Add Cancellation/Break
                parameter source: lesson dates
              kind: command
            - attributes:
                command: delete cancellation
                name: Remove Selected Cancellation/Break
                parameter source: cancellations
              kind: command
        - attributes:
            binding: additions
            header: Additions
            ref name: additions
          kind: list
          links:
            columns:
            - attributes:
                binding: date
                data type: date
                header: Addition Date
              kind: column
            - attributes:
                binding: reason
                header: Reason
              kind: column
            commands:
            - attributes:
                command: add addition
                name: Add Addition
              kind: command
            - attributes:
                command: delete addition
                name: Remove Selected Addition
                parameter source: additions
              kind: command
course list view:
  USES:
  - choose storage path
  - choose server
  - schedule to string converter
  - open course details
  - add new course
  - delete selected course
  attributes:
    height: 550
    width: 700
  kind: dock panel
  links:
    children:
    - attributes:
        dock: top
      kind: menu
      links:
        items:
        - attributes:
            header: File
          kind: menu item
          links:
            items:
            - attributes:
                command: choose storage path
                header: Choose storage path...
              kind: menu item
            - attributes:
                command: choose server
                header: Connect to server...
              kind: menu item
    - attributes:
        binding: courses
        font size: 12
        header: Course List
        read only: true
        ref name: course list
      kind: list
      links:
        columns:
        - attributes:
            binding: company
            header: Company
          kind: column
        - attributes:
            binding: group or student
            header: Group / Student
          kind: column
        - attributes:
            binding: course name
            header: Course
          kind: column
        - attributes:
            binding: schedule
            data converter: schedule to string converter
            header: Schedule
          kind: column
        - attributes:
            binding: number of lessons
            data type: integer
            header: '# Lessons'
          kind: column
        - attributes:
            binding: start date
            data type: date
            header: Start
          kind: column
        - attributes:
            binding: test dates
            binding index: 0
            data type: date
            header: Test 1
          kind: column
        - attributes:
            binding: test dates
            binding index: 1
            data type: date
            header: Test 2
          kind: column
        - attributes:
            binding: test dates
            binding index: 2
            data type: date
            header: Test 3
          kind: column
        - attributes:
            binding: test dates
            binding index: 3
            data type: date
            header: Test 4
          kind: column
        - attributes:
            binding: finish date
            data type: date
            header: Finish
          kind: column
        - attributes:
            binding: last update time
            data type: datetime
            header: Last Update
          kind: column
        commands:
        - attributes:
            command: open course details
            is default: true
            name: Show Selected Course Details
            no refresh: true
            parameter source: course list
          kind: command
        - attributes:
            command: add new course
            name: Add New Course
          kind: command
        - attributes:
            command: delete selected course
            name: Delete Selected Course
            parameter source: course list
          kind: command
course tracker context definition:
  data:
    courses: []
    courses history: []
    next course id: 1
  data change history:
    history: []
    last change number: 0
  settings:
    is networking: false
    server url: http://127.0.0.1:23423
    storage path: data/courses.yaml
create data from definition:
  RESULT:
  - data
  STEPS:
  - clone data:
      RESULT:
      - data
      TAKES:
      - AS: data
        PARAM: definition
  TAKES:
  - definition
create wpf application:
  RESULT:
  - application
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import Application
      application = Application()
      def handle_exception(s, e):
        execute_knowledge_piece('write .net exception to log', {'exception': e.Exception})
        e.Handled = True
      application.DispatcherUnhandledException += handle_exception
  USES:
  - write .net exception to log
create wpf binding:
  RESULT:
  - wpf binding
  STEPS:
  - EXEC: |
      def dummy(attributes):
       import clr
       clr.AddReferenceByPartialName('PresentationFramework')
       from System import Nullable
       from System.Windows import PropertyPath
       from System.Windows.Data import Binding
       from System.Windows.Data import BindingOperations
       from WebPythonHost import DelegateConverter
       from WebPythonHost import DelegatePropertyDescriptor
       from datetime import date
       from datetime import datetime

       def set_value(dict, key, value):
         dict[key] = value

       binding_name = attributes.get('binding')
       if not binding_name:
         return None
       binding_index = attributes.get('binding index')
       if binding_index is not None:
         #binding = Binding('.[%s][%d]' % (binding_name, binding_index))
         getter = lambda data: data[binding_name][binding_index]
         setter = lambda data, value: set_value(data[binding_name], binding_index, value)
         property_name = '%s[%d]' % (binding_name, binding_index)
       else:
         #binding = Binding('.[%s]' % binding_name)
         getter = lambda data: data[binding_name]
         setter = lambda data, value: set_value(data, binding_name, value)
         property_name = binding_name
       data_type_name = attributes.get('data type')
       data_type = None
       if data_type_name == 'integer':
         data_type = Nullable[int]
       elif data_type_name == 'date':
         data_type = date
       elif data_type_name == 'datetime':
         data_type = datetime
       property_descriptor = DelegatePropertyDescriptor(property_name, getter, setter, PropertyType_Setter = data_type)
       binding_path = PropertyPath('(0)', property_descriptor)
       converter = None
       converter_name = attributes.get('data converter')
       if converter_name:
         converter_def = knowledge_get(converter_name)
         if converter_def:
           forward_converter_name = converter_def['convert']
           back_converter_name = converter_def.get('convert back')
           forward_converter = (lambda value: execute_knowledge_piece(forward_converter_name, {'value': value})['converted_value']) if forward_converter_name else None
           back_converter = (lambda value: execute_knowledge_piece(back_converter_name, {'value': value})['converted_value']) if back_converter_name else None
           converter = DelegateConverter(forward_converter, back_converter)
       elif data_type_name == 'date':
         converter = DelegateConverter(lambda value: str(value) if value else '', lambda value: execute_knowledge_piece('convert string to date', {'value': value})['converted_value'])
       elif data_type_name == 'datetime':
         converter = DelegateConverter(lambda value: str(value) if value else '', lambda value: execute_knowledge_piece('convert string to datetime', {'value': value})['converted_value'])
       return Binding(Path = binding_path, Converter = converter, TargetNullValue = '')
      binding = dummy(attributes)
    RESULT:
    - AS: wpf binding
      PARAM: binding
    TAKES:
    - AS: attributes
      PARAM: binding attributes
  TAKES:
  - binding attributes
  USES:
  - convert string to date
  - convert string to datetime
create wpf element for column:
  RESULT:
  - wpf element
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      clr.AddReferenceByPartialName('WPFToolkit')
      #from System.Windows.Controls import DataGridTextColumn
      from Microsoft.Windows.Controls import DataGridTextColumn
      from System.Windows.Controls import TextBlock

      element = DataGridTextColumn(Header = TextBlock(Text = view['attributes']['header']))
      binding = execute_knowledge_piece('create wpf binding', {'binding attributes': view['attributes']})['wpf binding']
      if binding:
        element.Binding = binding
    RESULT:
    - AS: wpf element
      PARAM: element
  TAKES:
  - context
  - view
  USES:
  - create wpf binding
create wpf element for command:
  RESULT:
  - wpf element
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows.Controls import Button
      from System.Windows.Controls import TextBlock

      element = Button(Content = TextBlock(Text = view['attributes']['name']))
      command = view['attributes'].get('command')
      if command:
        parameter_source_name = view['attributes'].get('parameter source')
        element.Click += lambda sender, e: execute_knowledge_piece('execute command wpf', {
          'context': context,
          'command': command,
          'data context': element.DataContext,
          'parameter source name': parameter_source_name,
          'wpf element': element,
          'view attributes': view['attributes'],
        })
    RESULT:
    - AS: wpf element
      PARAM: element
  TAKES:
  - context
  - view
  USES:
  - execute command wpf
create wpf element for data field:
  RESULT:
  - wpf element
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import Thickness
      from System.Windows.Controls import StackPanel
      from System.Windows.Controls import TextBlock
      from System.Windows.Controls import TextBox
      from System.Windows.Data import BindingOperations

      element = StackPanel(Margin = Thickness(10,5,10,5))
      element.Children.Add(TextBlock(Text = view['attributes'].get('header')))
      binding = execute_knowledge_piece('create wpf binding', {'binding attributes': view['attributes']})['wpf binding']
      textbox = TextBox()
      textbox.IsReadOnly = bool(view['attributes'].get('read only'))
      if binding:
        BindingOperations.SetBinding(textbox, TextBox.TextProperty, binding)
      element.Children.Add(textbox)
    RESULT:
    - AS: wpf element
      PARAM: element
  TAKES:
  - context
  - view
  USES:
  - create wpf binding
create wpf element for dock panel:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import Thickness
      from System.Windows.Controls import Dock
      from System.Windows.Controls import DockPanel

      element = DockPanel(Margin = Thickness(10,5,10,5))
      def add_children(child_elements):
        for child_element, child in child_elements:
          #DockPanel.SetDock(child_element, Dock.Top)
          element.Children.Add(child_element)
      linkers = {
        'children': lambda parent, child_elements: add_children(child_elements),
      }
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
create wpf element for horizontal grid:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import HorizontalAlignment
      from System.Windows import GridLength
      from System.Windows import VerticalAlignment
      from System.Windows.Controls import ColumnDefinition
      from System.Windows.Controls import Grid
      from System.Windows.Controls import GridSplitter

      element = Grid()
      def add_children(child_elements):
        for child_element, child in child_elements:
          if element.Children.Count > 0:
            splitter = GridSplitter(VerticalAlignment = VerticalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Center, Width = 3)
            Grid.SetColumn(splitter, element.Children.Count)
            element.ColumnDefinitions.Add(ColumnDefinition(Width = GridLength.Auto))
            element.Children.Add(splitter)
          Grid.SetColumn(child_element, element.Children.Count)
          element.ColumnDefinitions.Add(ColumnDefinition())
          element.Children.Add(child_element)
      linkers = {
        'children': lambda parent, child_elements: add_children(child_elements),
      }
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
create wpf element for list:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      def dummy(context, view):
        import clr
        clr.AddReferenceByPartialName('PresentationFramework')
        clr.AddReferenceByPartialName('WPFToolkit')
        from System.Windows import EventSetter
        from System.Windows import HorizontalAlignment
        from System.Windows import Setter
        from System.Windows import Style
        from System.Windows import Thickness
        from System.Windows import VerticalAlignment
        #from System.Windows.Controls import DataGrid
        #from System.Windows.Controls import DataGridCell
        #from System.Windows.Controls import DataGridSelectionUnit
        from Microsoft.Windows.Controls import DataGrid
        from Microsoft.Windows.Controls import DataGridCell
        from Microsoft.Windows.Controls import DataGridSelectionUnit
        from System.Windows.Controls import Dock
        from System.Windows.Controls import DockPanel
        from System.Windows.Controls import Grid
        from System.Windows.Controls import TextBlock
        from System.Windows.Controls import UserControl
        from System.Windows.Controls import WrapPanel
        from System.Windows.Data import BindingOperations
        from System.Windows.Input import MouseButtonEventHandler
        from System.ComponentModel import ListSortDirection

        class MyDataGrid(UserControl):
          def __init__(self, view):
            self.commands = []
            self.default_command = None

            grid = Grid(HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top)
            dockPanel = DockPanel()
            grid.Children.Add(dockPanel)
            header = view['attributes'].get('header')
            if header:
              headerTextBlock = TextBlock(Text = header, Margin = Thickness(2,2,2,2))
              DockPanel.SetDock(headerTextBlock, Dock.Top)
              dockPanel.Children.Add(headerTextBlock)
            dataGrid = DataGrid()
            dockPanel.Children.Add(dataGrid)
            dataGrid.CanUserAddRows = False
            dataGrid.CanUserResizeRows = False
            dataGrid.CanUserSortColumns = True
            dataGrid.SelectionUnit = DataGridSelectionUnit.CellOrRowHeader
            buttonPanel = WrapPanel()
            DockPanel.SetDock(buttonPanel, Dock.Bottom)
            dockPanel.Children.Insert(0, buttonPanel)
            cellStyle = Style(TargetType = DataGridCell)
            cellStyle.Setters.Add(EventSetter(Event = DataGridCell.MouseDoubleClickEvent, Handler = MouseButtonEventHandler(lambda sender, e: element.on_double_click(sender, e))))
            dataGrid.CellStyle = cellStyle
            dataGrid.Sorting += self.on_sorting
            self.dataGrid = dataGrid
            self.buttonPanel = buttonPanel
            self.Content = grid
            self.ItemsBinding = None
          def get_DataGrid(self):
            return self.dataGrid
          def add_command(self, command_element):
            self.buttonPanel.Children.Add(command_element)
          def on_sorting(self, sender, e):
            prevSortDir = e.Column.SortDirection
            newSortDir = ListSortDirection.Descending if prevSortDir == ListSortDirection.Ascending else (ListSortDirection.Ascending if prevSortDir is None else None)
            if newSortDir is not None:
              binding = e.Column.Binding
              path = binding.Path
              propDesc = path.PathParameters[0]
              getter = propDesc.ValueGetter
              items = list(sorted(self.dataGrid.ItemsSource, key = lambda item: getter(item)))
              if newSortDir == ListSortDirection.Descending:
                items.reverse()
              self.dataGrid.ItemsSource = items
            else:
              BindingOperations.SetBinding(self.dataGrid, DataGrid.ItemsSourceProperty, self.ItemsBinding)
            e.Column.SortDirection = newSortDir
            e.Handled = True
          def on_double_click(self, sender, e):
            if self.default_command:
              self.default_command(sender, e)
          def get_selected_item(self):
            #return self.get_DataGrid().SelectedItem
            cells = self.get_DataGrid().SelectedCells
            cell = cells.Count > 0 and cells[0]
            return cell and cell.Item
          def get_selected_items(self):
            cells = self.get_DataGrid().SelectedCells
            distinct_ids = set()
            items = []
            for cell in cells:
              item = cell.Item
              item_id = id(item)
              if item_id not in distinct_ids:
                items.append(item)
                distinct_ids.add(item_id)
            return items

        element = MyDataGrid(view)
        dataGrid = element.get_DataGrid()
        dataGrid.IsReadOnly = bool(view['attributes'].get('read only'))
        binding = execute_knowledge_piece('create wpf binding', {'binding attributes': view['attributes']})['wpf binding']
        if binding:
          element.ItemsBinding = binding
          BindingOperations.SetBinding(dataGrid, DataGrid.ItemsSourceProperty, binding)
        def add_columns(child_elements):
          for child_element, child in child_elements:
            dataGrid.Columns.Add(child_element)
        def add_commands(child_elements):
          for child_element, child in child_elements:
            element.add_command(child_element)
            #write_to_log(to_html(to_yaml(child)))
            if child['attributes'].get('is default'):
              command_attributes = child['attributes']
              command = command_attributes.get('command')
              parameter_source_name = command_attributes.get('parameter source')
              element.default_command = lambda sender, e: execute_knowledge_piece('execute command wpf', {
                'context': context,
                'command': command,
                'data context': element.DataContext,
                'parameter source name': parameter_source_name,
                'wpf element': element,
                'view attributes': command_attributes,
              })
        linkers = {
          'columns': lambda parent, child_elements: add_columns(child_elements),
          'commands': lambda parent, child_elements: add_commands(child_elements),
        }
        return (element, linkers)
      element, linkers = dummy(context, view)
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
  USES:
  - create wpf binding
  - execute command wpf
create wpf element for menu:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows.Controls import Menu

      element = Menu()

      def add_children(child_elements):
        for child_element, child in child_elements:
          element.Items.Add(child_element)
      linkers = {
        'items': lambda parent, child_elements: add_children(child_elements),
      }
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
create wpf element for menu item:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows.Controls import MenuItem
      from System.Windows.Controls import TextBlock

      header = view['attributes']['header']
      element = MenuItem(Header = TextBlock(Text = header))
      command = view['attributes'].get('command')
      if command:
        parameter_source_name = view['attributes'].get('parameter source')
        element.Click += lambda sender, e: execute_knowledge_piece('execute command wpf', {
          'context': context,
          'command': command,
          'data context': element.DataContext,
          'parameter source name': parameter_source_name,
          'wpf element': element,
          'view attributes': view['attributes'],
        })

      def add_children(child_elements):
        for child_element, child in child_elements:
          element.Items.Add(child_element)
      linkers = {
        'items': lambda parent, child_elements: add_children(child_elements),
      }
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
  USES:
  - execute command wpf
create wpf element for unknown:
  RESULT:
  - wpf element
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows.Controls import TextBlock

      element = TextBlock(Text = 'Неизвестный тип элемента: ' + view['kind'])
    RESULT:
    - AS: wpf element
      PARAM: element
  TAKES:
  - view
create wpf element for vertical grid:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import HorizontalAlignment
      from System.Windows import GridLength
      from System.Windows import VerticalAlignment
      from System.Windows.Controls import Grid
      from System.Windows.Controls import GridSplitter
      from System.Windows.Controls import RowDefinition

      element = Grid()
      def add_children(child_elements):
        for child_element, child in child_elements:
          if element.Children.Count > 0:
            splitter = GridSplitter(VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Stretch, Height = 3)
            Grid.SetRow(splitter, element.Children.Count)
            element.RowDefinitions.Add(RowDefinition(Height = GridLength.Auto))
            element.Children.Add(splitter)
          Grid.SetRow(child_element, element.Children.Count)
          element.RowDefinitions.Add(RowDefinition())
          element.Children.Add(child_element)
      linkers = {
        'children': lambda parent, child_elements: add_children(child_elements),
      }
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
create wpf element for wrap panel:
  RESULT:
  - wpf element
  - child linkers
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import Thickness
      from System.Windows.Controls import WrapPanel

      element = WrapPanel(Margin = Thickness(10,5,10,5))
      def add_children(child_elements):
        for child_element, child in child_elements:
          element.Children.Add(child_element)
      linkers = {
        'children': lambda parent, child_elements: add_children(child_elements),
      }
    RESULT:
    - AS: wpf element
      PARAM: element
    - AS: child linkers
      PARAM: linkers
  TAKES:
  - context
  - view
date to date with weekday converter:
  USES:
  - convert date to date with weekday
  convert: convert date to date with weekday
default if None:
  RESULT:
  - value
  STEPS:
  - EXEC: value = value if value is not None else default_value
  TAKES:
  - value
  - AS: default_value
    PARAM: default value
delete addition:
  STEPS:
  - EXEC: |
      data['additions'].remove(selected_data)
    TAKES:
    - AS: selected_data
      PARAM: selected data
    - data
  TAKES:
  - data
  - selected data
  USES:
  - course definition
delete cancellation:
  STEPS:
  - EXEC: |
      data['cancellations'].remove(selected_data)
    TAKES:
    - AS: selected_data
      PARAM: selected data
    - data
  TAKES:
  - data
  - selected data
  USES:
  - course definition
delete course tracker course: null
delete from list:
  STEPS:
  - EXEC: |-
      index = -1
      for i in range(len(list)):
        item = list[i]
        if key in item and item[key] == key_value:
          index = i
          break
      if index >= 0:
        del list[index]
  TAKES:
  - list
  - key
  - AS: key_value
    PARAM: key value
delete selected course:
  OLD STEPS:
  - set value:
      TAKES:
      - AS: dictionary
        PARAM: selected data
      - AS: key
        LITERAL: deleted
      - AS: value
        LITERAL: true
  - get value:
      RESULT:
      - AS: course tracker data
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL:
        - app context
        - data
  - get value:
      RESULT:
      - AS: courses
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: course tracker data
      - AS: key
        LITERAL: courses
  - get value:
      RESULT:
      - AS: course id
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: selected data
      - AS: key
        LITERAL: id
  - delete from list:
      TAKES:
      - AS: list
        PARAM: courses
      - AS: key
        LITERAL: id
      - AS: key value
        PARAM: course id
  - get value:
      RESULT:
      - AS: courses history
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: course tracker data
      - AS: key
        LITERAL: courses history
  - clone data:
      RESULT:
      - AS: cloned course data
        PARAM: data
      TAKES:
      - AS: data
        PARAM: selected data
  - append to list:
      TAKES:
      - AS: list
        PARAM: courses history
      - AS: value
        PARAM: cloned course data
  - get value:
      RESULT:
      - AS: app context
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL: app context
  - save course tracker data:
      TAKES:
      - AS: context
        PARAM: app context
      - AS: course tracker data
        PARAM: course tracker data
  STEPS:
  - clone data:
      RESULT:
      - course data: data
      TAKES:
      - data: selected data
  - set value:
      TAKES:
      - dictionary: course data
      - AS: key
        LITERAL: deleted
      - AS: value
        LITERAL: true
  - save course data:
      TAKES:
      - context
      - data: course data
  TAKES:
  - context
  - selected data
do alternatively:
  RESULT:
  - results
  STEPS:
  - EXEC: |
      results = {}
      for alternative_action in alternative_actions:
        value = alternative_action['VALUE']
        if value == alternative_value:
          action_alias = alternative_action['ACTION']
          parameters = {
            'context': context,
          }
          if type(context) is dict:
            parameters.update(context)
          results = execute_knowledge_piece(action_alias, parameters)
          break
  TAKES:
  - context
  - alternative_value
  - alternative_actions
execute command wpf:
  STEPS:
  - EXEC: |
      try:
        #parameter_source = element.FindName(parameter_source_name)
        parameter_source = parameter_source_name and context.get('name scope', {}).get(parameter_source_name)
        selected_data = parameter_source and execute_knowledge_piece('get selected data from list', {'data source': parameter_source})['selected data']
        handled = False
        if parameter_source_name and not selected_data:
          on_selection_empty = view_attrbutes.get('on selection empty')
          if on_selection_empty:
            execute_knowledge_piece('execute command wpf', {
              'context': context,
              'command': on_selection_empty['command'],
              'data context': data_context,
              'parameter source name': on_selection_empty.get('parameter source'),
              'wpf element': element,
              'view attributes': on_selection_empty,
            })
          handled = True
        if not handled:
          selected_items = parameter_source and parameter_source.get_selected_items()
          execute_knowledge_piece(command, {
            'context': context,
            'data': data_context,
            'parameter_source': parameter_source,
            'selected data': selected_data,
            'selected items': selected_items})
          if view_attrbutes.get('type') == 'submit':
            context['window'].Close()
            parent_view_context = context.get('parent view context')
            if parent_view_context:
              refresh = parent_view_context['refresh']
              refresh(parent_view_context)
          elif not view_attrbutes.get('no refresh'):
            #write_to_log('<br/><br/><b>%s</b><br/><br/>' % to_html(repr(context)))
            refresh = context['refresh']
            refresh(context)
      except:
        execute_knowledge_piece('write python exception to log', {})
  TAKES:
  - context
  - command
  - AS: data_context
    PARAM: data context
  - AS: parameter_source_name
    PARAM: parameter source name
  - AS: element
    PARAM: wpf element
  - AS: view_attrbutes
    PARAM: view attributes
  USES:
  - get selected data from list
  - write python exception to log
  - execute command wpf
execute knowledge piece:
  RESULT:
  - results
  STEPS:
  - EXEC: |
      def execute_knowledge_piece_EXEC(knowledge_piece_alias, knowledge_piece_parameters):
        if is_debug:
          write_to_log('<div><b>%s</b></div>' % knowledge_piece_alias)

        knowledge_piece = knowledge_get(knowledge_piece_alias)
        if not knowledge_piece:
          raise Exception('"%s" not found' % knowledge_piece_alias)

        def get_params(param_defs, knowledge_piece_parameters):
          params = {}
          for p in param_defs:
            if type(p) is dict and 'AS' in p:
              as_ = p['AS']
              if 'LITERAL' in p:
                params[as_] = p['LITERAL']
              elif 'KNOWLEDGE' in p:
                params[as_] = knowledge_get(p['KNOWLEDGE'])
              elif 'PARAM' in p:
                params[as_] = knowledge_piece_parameters.get(p['PARAM'])
              elif 'WHAT' in p: # legacy
                what = p["WHAT"]
                if what in knowledge_piece_parameters:
                  value = knowledge_piece_parameters.get(what)
                else:
                  value = knowledge_get(what)
                if value is None:
                  value = what
                params[as_] = value
            elif type(p) is dict and len(p) == 1:
              pair = p.items()[0]
              params[pair[0]] = knowledge_piece_parameters[pair[1]]
            elif type(p) is str or type(p) is unicode:
              params[p] = knowledge_piece_parameters[p]
            else:
              raise Exception('unrecognized step param: %s' % repr(p))
          return params

        parameters = get_params(knowledge_piece.get('TAKES', []), knowledge_piece_parameters)

        if is_debug:
          write_to_log(u'<div>Входные параметры: <pre>%s</pre></div><ul>' % html_encode(to_yaml(parameters)))

        steps = knowledge_piece["STEPS"]

        def process_step(step, parameters):
          if is_debug:
            write_to_log(u'<li><div>Вызов шага: <pre>%s</pre></div>' % html_encode(to_yaml(step)))
          exec_code = None
          step_conditions = None
          if type(step) is dict and len(step) > 0:
            if 'EXEC' not in step:
              step_pair = step.items()[0]
              alias = step_pair[0]
              step_conditions = step_pair[1]
            else:
              alias = 'EXEC'
              exec_code = step.get('EXEC')
              step_conditions = step
          else:
            alias = step
          step_parameters = {}
          if type(step_conditions) is dict:
            if alias == 'EXEC' and 'TAKES' not in step_conditions:
              step_parameters = parameters
            else:
              step_parameters = get_params(step_conditions.get('TAKES', []), parameters)
          if is_debug:
            write_to_log(u'<div>Входные параметры шага: <pre>%s</pre></div>' % html_encode(to_yaml(step_parameters)))
          if alias == 'EXEC':
            code = exec_code
            exec code in globals(), step_parameters
            step_results = step_parameters
          else:
            step_results = execute_knowledge_piece(alias, step_parameters)
          if is_debug:
            write_to_log(u'<div>Все результаты шага %s: <pre>%s</pre></div>' % (alias, html_encode(to_yaml(step_results))))
          if type(step_conditions) is dict:
            if alias == 'EXEC' and 'RESULT' not in step_conditions:
              step_results = step_results
            else:
              step_results = get_params(step_conditions.get('RESULT', []), step_results)
          if is_debug:
            write_to_log(u'<div>Выбранные результаты шага %s: <pre>%s</pre></div></li>' % (alias, html_encode(to_yaml(step_results))))
          parameters.update(step_results)
          if is_debug:
            write_to_log(u'<div>Внутренние параметры после шага %s: <pre>%s</pre></div>' % (alias, html_encode(to_yaml(parameters))))

        for step in steps:
          process_step(step, parameters)

        #if type(knowledge_piece_parameters) is dict:
        #  knowledge_piece_parameters.update(results)

        results = get_params(knowledge_piece.get('RESULT', []), parameters)
        if is_debug:
          write_to_log(u'</ul><div>Выбранные результаты %s: <pre>%s</pre></div>' % (knowledge_piece_alias, html_encode(to_yaml(results))))
        return results
      results = execute_knowledge_piece_EXEC(knowledge_piece_alias, knowledge_piece_parameters)
  TAKES:
  - knowledge_piece_alias
  - knowledge_piece_parameters
get change list:
  RESULT:
  - change list
  STEPS:
  - EXEC: |-
      if len(history) < end - start:
        change_list = [data]
      else:
        change_list = history[len(history) - (end - start):len(history)]
    RESULT:
    - change list: change_list
  TAKES:
  - data
  - history
  - start
  - end
get changes:
  EXAMPLE:
    changes1:
    - delete: 2
    - add: 5
    changes2:
    - change:
        at: 1
        from: 2
        to: 3
    - change:
        at: 2
        from: 3
        to: 4
    - change:
        at: 3
        from: 4
        to: 5
    changes3:
    - 1
    - 3
    - 4
    - 5
    data:
    - 1
    - 3
    - 4
    - 5
    previous data:
    - 1
    - 2
    - 3
    - 4
  RESULT:
  - changes
  STEPS:
  - EXEC: |
      if type(data) is dict:
        if type(prev_data) is not dict:
          changes = data
        else:
          changes = {}
          prev_keys = set(prev_data.iterkeys())
          for key, value in data.iteritems():
            if key not in prev_keys:
              changes[key] = value
            else:
              prev_keys.remove(key)
              item_changes = execute_knowledge_piece('get changes', {'data': value, 'previous data': prev_data[key]})['changes']
              if item_changes:
                changes[key] = item_changes
          for key in prev_keys:
            changes[key] = None
      elif type(data) is list:
        if type(prev_data) is not list:
          changes = data
        else:
          changes = []
          prev_items_with_id = dict([(item['id'], item) for item in prev_data if type(item) is dict and 'id' in item])
          for item in data:
            if type(item) is dict and 'id' in item:
              id = item['id']
              prev_item = prev_items_with_id.pop(id, None)
              if prev_item is not None:
                item_changes = execute_knowledge_piece('get changes', {'data': item, 'previous data': prev_item})['changes']
                if item_changes:
                  item_changes['id'] = id
                  changes.append(item_changes)
              else:
                changes.append(item)
            else:
              changes.append(item)
          for id, item in prev_items_with_id.iteritems():
            changes.append({'id': id, 'deleted': True})
      else:
        if data != prev_data:
          changes = data
        else:
          changes = None
    RESULT:
    - changes
    TAKES:
    - data
    - prev_data: previous data
  TAKES:
  - previous data
  - data
  USES:
  - get changes
get course tracker courses:
  RESULT:
  - response
  STEPS:
  - get value:
      RESULT:
      - response: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL: data
  TAKES:
  - context
  - request
get course tracker data change history path:
  RESULT:
  - history path
  STEPS:
  - EXEC: |-
      from System.IO import Path
      hist_path = Path.ChangeExtension(path, 'history.yaml')
    RESULT:
    - history path: hist_path
    TAKES:
    - path: storage path
  TAKES:
  - storage path
get course tracker data storage path:
  RESULT:
  - storage path
  STEPS:
  - get value:
      RESULT:
      - storage path: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - settings
        - storage path
  - default if None:
      RESULT:
      - storage path: value
      TAKES:
      - value: storage path
      - AS: default value
        LITERAL: data/courses.yaml
  - map path relative to application:
      RESULT:
      - storage path: path
      TAKES:
      - path: storage path
  TAKES:
  - context
get current time:
  RESULT:
  - current time
  STEPS:
  - EXEC: |
      from datetime import datetime
      current_time = datetime.utcnow()
    RESULT:
    - AS: current time
      PARAM: current_time
get item by id:
  RESULT:
  - item
  STEPS:
  - EXEC: |
      item = None
      for it in list:
        if it.get(id_name) == id:
          item = it
          break
    RESULT:
    - item
    TAKES:
    - AS: id_name
      PARAM: id definition
    - AS: id
      PARAM: id value
    - list
  TAKES:
  - list
  - id definition
  - id value
get length:
  RESULT:
  - length
  STEPS:
  - EXEC: length = len(collection)
  TAKES:
  - collection
get selected data from list:
  RESULT:
  - AS: selected data
    PARAM: data
  STEPS:
  - EXEC: 'data = data_source.get_selected_item() # hack: using internal knowledge'
  TAKES:
  - AS: data_source
    PARAM: data source
get value:
  RESULT:
  - value
  STEPS:
  - EXEC: |
      if type(key) is not list:
        key = [key]
      value = dictionary
      for key_part in key:
        if type(value) is dict:
          value = value.get(key_part, None)
        elif value is not None:
          value = value[key_part]
  TAKES:
  - dictionary
  - key
get yaml data path:
  RESULT:
  - data_path
  STEPS:
  - EXEC: |
      from WebPythonHost import ApplicationHlp
      from System.IO import Path
      dir = ApplicationHlp.MapPath('data')
      data_path = Path.Combine(dir, data_name + '.yaml')
  TAKES:
  - data_name
handle course tracker server request:
  OLD:
  - EXEC: |
      header = 'HTTP/1.0 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n'
      response = header + 'request is ' + request
  RESULT:
  - response text
  STEPS:
  - load yaml in python:
      RESULT:
      - request: result
      TAKES:
      - yaml_content: request text
  - get value:
      RESULT:
      - command: value
      TAKES:
      - dictionary: request
      - AS: key
        LITERAL: command
  - make data:
      RESULT:
      - parameters: data
      TAKES:
      - AS: template
        LITERAL:
          context: param1
          request: param2
      - param1: context
      - param2: request
      - AS: param3
        LITERAL: null
  - do alternatively:
      RESULT:
      - results
      TAKES:
      - context: parameters
      - alternative_value: command
      - AS: alternative_actions
        LITERAL:
        - ACTION: get course tracker courses
          VALUE: get courses
        - ACTION: handle sync course tracker changes request
          VALUE: sync changes
        - ACTION: get course tracker courses
          VALUE: null
  - get value:
      RESULT:
      - response: value
      TAKES:
      - dictionary: results
      - AS: key
        LITERAL: response
  - convert to yaml:
      RESULT:
      - response text: yaml
      TAKES:
      - kpiece: response
  TAKES:
  - context
  - request text
handle sync course tracker changes request:
  EXAMPLES:
  - request:
      command: sync changes
      data:
        change list:
        - changes:
            courses:
            - company: comp1
              id: 1
          last change number: 2
        last change number: 3
  RESULT:
  - response
  STEPS:
  - get value:
      RESULT:
      - change list: value
      TAKES:
      - dictionary: request
      - AS: key
        LITERAL:
        - data
        - change list
  - get value:
      RESULT:
      - last change number: value
      TAKES:
      - dictionary: request
      - AS: key
        LITERAL:
        - data
        - last change number
  - EXEC: |
      from System.Threading import Monitor
      Monitor.Enter(context)
      try:
        results = execute_knowledge_piece('sync course tracker changes', {
          'context': context,
          'change list': change_list,
          'last change number': last_change_number,
        })
        change_list = results['change list']
        last_change_number = results['last change number']
      finally:
        Monitor.Exit(context)
    RESULT:
    - change list: change_list
    - last change number: last_change_number
    TAKES:
    - context
    - change_list: change list
    - last_change_number: last change number
  - make data:
      RESULT:
      - response: data
      TAKES:
      - AS: template
        LITERAL:
          change list: param1
          last change number: param2
      - param1: change list
      - param2: last change number
      - AS: param3
        LITERAL: null
  TAKES:
  - context
  - request
  TEMP STEPS:
  - sync course tracker changes:
      RESULT:
      - change list
      - last change number
      TAKES:
      - context
      - change list
      - last change number
  USES:
  - sync course tracker changes
increment:
  RESULT:
  - value
  STEPS:
  - EXEC: value = value + 1
  TAKES:
  - value
load .net host settings:
  RESULT:
  - settings
  STEPS:
  - EXEC: |
      import clr
      #clr.AddReferenceByPartialName('WebPythonHost')
      from WebPythonHost import ApplicationHlp
      path = ApplicationHlp.MapPath('Settings.config')

      clr.AddReferenceByPartialName('System.Xml.Linq')
      from System.Xml.Linq import XElement
      xElement = XElement.Load(path)
      settings = dict([(child.Name.LocalName, child.Value) for child in xElement.Elements()])
load course tracker data:
  RESULT:
  - course tracker data
  STEPS:
  - get value:
      RESULT:
      - is networking: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - settings
        - is networking
  - do alternatively:
      RESULT:
      - results
      TAKES:
      - context
      - alternative_value: is networking
      - AS: alternative_actions
        LITERAL:
        - ACTION: load course tracker data from server
          VALUE: true
        - ACTION: load course tracker data from file
          VALUE: false
        - ACTION: load course tracker data from file
          VALUE: null
  - get value:
      RESULT:
      - course tracker data: value
      TAKES:
      - dictionary: results
      - AS: key
        LITERAL: course tracker data
  TAKES:
  - context
load course tracker data from file:
  RESULT:
  - course tracker data
  STEPS:
  - get course tracker data storage path:
      RESULT:
      - storage path
      TAKES:
      - context
  - load yaml data from file:
      RESULT:
      - course tracker data: data
      TAKES:
      - path: storage path
  TAKES:
  - context
load course tracker data from server:
  RESULT:
  - course tracker data
  STEPS:
  - send course tracker request to server:
      RESULT:
      - course tracker data: response
      TAKES:
      - context
      - AS: request
        LITERAL:
          command: get courses
  TAKES:
  - context
load course tracker settings:
  RESULT:
  - settings
  STEPS:
  - map path relative to application:
      RESULT:
      - path
      TAKES:
      - AS: path
        LITERAL: settings.yaml
  - load yaml data from file:
      RESULT:
      - AS: settings
        PARAM: data
      TAKES:
      - path
  - default if None:
      RESULT:
      - AS: settings
        PARAM: value
      TAKES:
      - AS: value
        PARAM: settings
      - AS: default value
        LITERAL:
          storage path: data/courses.yaml
  - set value:
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL: settings
      - AS: value
        PARAM: settings
  TAKES:
  - context
  USES:
  - course tracker context definition
load data by name:
  RESULT:
  - data
  STEPS:
  - get yaml data path:
      RESULT:
      - data_path
      TAKES:
      - data_name
  - load yaml data from file:
      RESULT:
      - data
      TAKES:
      - AS: path
        PARAM: data_path
  TAKES:
  - data_name
load yaml data from file:
  RESULT:
  - data
  STEPS:
  - read file text:
      RESULT:
      - text
      TAKES:
      - path
  - load yaml in python:
      RESULT:
      - AS: data
        PARAM: result
      TAKES:
      - AS: yaml_content
        PARAM: text
  TAKES:
  - path
load yaml in python:
  RESULT:
  - result
  STEPS:
  - EXEC: |
      if yaml_content is None:
        result = None
      else:
        import yaml
        result = yaml.load(yaml_content)
  TAKES:
  - yaml_content
make data:
  RESULT:
  - data
  STEPS:
  - EXEC: |
      params = {
        'param1': param1,
        'param2': param2,
        'param3': param3,
      }
      def dummy(template, params):
        make_data = lambda template: execute_knowledge_piece('make data', dict(params.items() + {'template': template}.items()))['data']
        if type(template) is dict:
          data = dict((item[0], make_data(item[1])) for item in template.iteritems())
        elif type(template) is list:
          data = [make_data(item) for item in template]
        else:
          data = params.get(template, template)
        return data
      data = dummy(template, params)
  TAKES:
  - template
  - param1
  - param2
  - param3
make wpf element from view:
  RESULT:
  - element
  STEPS:
  - EXEC: |
      def dummy(view, context):
        def process_links(context, view, element, linkers):
          links = view.get('links', [])
          if type(links) is dict:
            links = [{'kind': kind, 'elements': elements} for kind, elements in links.iteritems()]
          for link in links:
            linker = linkers.get(link['kind'], None)
            if linker:
              childs = link.get('elements', [])
              child_elements = []
              for child in childs:
                child_element = execute_knowledge_piece('make wpf element from view', {'context': context, 'view': child})['element']
                child_elements.append((child_element, child))
              linker(element, child_elements)

        view_kind = view['kind']
        if view_kind not in view_definition['kinds']:
          view_kind = 'unknown'
        result = execute_knowledge_piece('create wpf element for ' + view_kind, {'context': context, 'view': view})
        element = result['wpf element']
        linkers = result.get('child linkers')

        attributes = view.get('attributes')
        ref_name = attributes and attributes.get('ref name')
        if ref_name:
          name_scope = context.setdefault('name scope', {})
          name_scope[ref_name] = element
          element.Name = ref_name.replace(' ', '_')
          #element.RegisterName(ref_name, element)

        execute_knowledge_piece('apply common attributes to wpf element', {'wpf element': element, 'attributes': attributes})

        process_links(context, view, element, linkers if linkers is not None else {})

        return element
      element = dummy(view, context)
  TAKES:
  - view
  - context
  - AS: view_definition
    KNOWLEDGE: view definition
  USES:
  - make wpf element from view
  - apply common attributes to wpf element
  - create wpf element for unknown
  - create wpf element for list
  - create wpf element for column
  - create wpf element for command
  - create wpf element for dock panel
  - create wpf element for wrap panel
  - create wpf element for horizontal grid
  - create wpf element for vertical grid
  - create wpf element for data field
  - create wpf element for menu
  - create wpf element for menu item
map path relative to application:
  RESULT:
  - path
  STEPS:
  - EXEC: |
      from System.IO import Path
      from WebPythonHost import ApplicationHlp
      if not Path.IsPathRooted(path):
        path = ApplicationHlp.MapPath(path)
  TAKES:
  - path
open course details:
  STEPS:
  - get value:
      RESULT:
      - AS: app context
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL: app context
  - clone data:
      RESULT:
      - data
      TAKES:
      - AS: data
        PARAM: selected data
  - calculate course schedule data:
      RESULT:
      - data
      TAKES:
      - data
  - open view:
      TAKES:
      - AS: context
        PARAM: app context
      - AS: data
        PARAM: data
      - AS: view
        KNOWLEDGE: course details view
      - AS: title
        LITERAL: Course Details
      - AS: parent context
        PARAM: context
  TAKES:
  - context
  - selected data
open view:
  RESULT:
  - window
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationCore')
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.Windows import SizeToContent
      from System.Windows import Window
      from System.Windows.Controls import ScrollViewer
      from System.Windows.Controls import ScrollBarVisibility
      window = Window()
      window.Title = title
      view_attributes = view.get('attributes', {})
      view_width = view_attributes.pop('width', None)
      view_height = view_attributes.pop('height', None)
      if view_width and view_height:
        window.Width = view_width
        window.Height = view_height
      elif view_width:
        window.Width = view_width
        window.SizeToContent = SizeToContent.Height
      elif view_height:
        window.Height = view_height
        window.SizeToContent = SizeToContent.Width
      else:
        window.SizeToContent = SizeToContent.WidthAndHeight
      #window.Content = ScrollViewer(VerticalScrollBarVisibility = ScrollBarVisibility.Auto)
      def refresh(view_context):
        view_context['window'].DataContext = view_context['data']
        view_context['window'].Content = execute_knowledge_piece('make wpf element from view', {'view': view_context['view'], 'context': view_context})['element']
      view_context = {'app context': context, 'window': window, 'data': data, 'view': view, 'refresh': refresh, 'parent view context': parent_context}
      refresh(view_context)
      window.Show()
  TAKES:
  - context
  - view
  - data
  - title
  - AS: parent_context
    PARAM: parent context
  USES:
  - make wpf element from view
read file text:
  RESULT:
  - text
  STEPS:
  - EXEC: |
      from System.IO import File
      if File.Exists(path):
        text = File.ReadAllText(path)
      else:
        text = None
  TAKES:
  - path
reload course tracker data:
  RESULT:
  - course tracker data
  STEPS:
  - load course tracker data:
      RESULT:
      - course tracker data
      TAKES:
      - context
  - default if None:
      RESULT:
      - AS: course tracker data
        PARAM: value
      TAKES:
      - AS: value
        PARAM: course tracker data
      - AS: default value
        LITERAL:
          courses: []
          courses history: []
          next course id: 1
  - set value:
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL: data
      - AS: value
        PARAM: course tracker data
  TAKES:
  - context
  USES:
  - course tracker context definition
run application:
  STEPS:
  - EXEC: |
      if window:
        application.Run(window)
      else:
        application.Run()
  TAKES:
  - application
  - window
run course tracker:
  STEPS:
  - create data from definition:
      RESULT:
      - AS: context
        PARAM: data
      TAKES:
      - AS: definition
        KNOWLEDGE: course tracker context definition
  - load course tracker settings:
      RESULT:
      - settings
      TAKES:
      - context
  - create wpf application:
      RESULT:
      - application
  - reload course tracker data:
      RESULT:
      - course tracker data
      TAKES:
      - context
  - open view:
      RESULT:
      - window
      TAKES:
      - context
      - AS: view
        KNOWLEDGE: course list view
      - AS: data
        PARAM: course tracker data
      - AS: title
        LITERAL: Course Tracker
      - AS: parent context
        LITERAL: null
  - run application:
      TAKES:
      - application
      - window
run course tracker server:
  STEPS:
  - create data from definition:
      RESULT:
      - AS: context
        PARAM: data
      TAKES:
      - AS: definition
        KNOWLEDGE: course tracker context definition
  - load course tracker settings:
      RESULT:
      - settings
      TAKES:
      - context
  - create wpf application:
      RESULT:
      - application
  - reload course tracker data:
      RESULT:
      - course tracker data
      TAKES:
      - context
  - start server:
      TAKES:
      - context
      - AS: handler
        LITERAL: handle course tracker server request
      - AS: port
        LITERAL: 23423
  - run application:
      TAKES:
      - application
      - AS: window
        LITERAL: null
  USES:
  - handle course tracker server request
russian holidays:
- end date: 2010-01-08
  reason: Новогодние каникулы и Рождество
  start date: 2010-01-01
- end date: 2010-02-23
  extra working date: 2010-02-27
  reason: День защитника Отечества
  start date: 2010-02-22
- end date: 2010-03-08
  reason: Международный женский день
  start date: 2010-03-08
- end date: 2010-05-03
  reason: Праздник Весны и Труда
  start date: 2010-05-01
- end date: 2010-05-10
  reason: День Победы
  start date: 2010-05-09
- end date: 2010-06-14
  reason: День России
  start date: 2010-06-12
- end date: 2010-11-05
  extra working date: 2010-11-13
  reason: День народного единства
  start date: 2010-11-04
- end date: 2011-01-10
  reason: Новогодние каникулы и Рождество
  start date: 2011-01-01
- end date: 2011-02-23
  reason: День защитника Отечества
  start date: 2011-02-23
- end date: 2011-03-08
  extra working date: 2011-03-05
  reason: Международный женский день
  start date: 2011-03-07
- end date: 2011-05-02
  reason: Праздник Весны и Труда
  start date: 2011-05-01
- end date: 2011-05-09
  reason: День Победы
  start date: 2011-05-09
- end date: 2011-06-13
  reason: День России
  start date: 2011-06-12
- end date: 2011-11-04
  reason: День народного единства
  start date: 2011-11-04
save course data:
  STEPS:
  - get current time:
      RESULT:
      - current time
  - set value:
      TAKES:
      - dictionary: course data
      - AS: key
        LITERAL: last update time
      - value: current time
  - get value:
      RESULT:
      - context: value
      TAKES:
      - dictionary: view context
      - AS: key
        LITERAL: app context
  - get value:
      RESULT:
      - data: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL: data
  - get value:
      RESULT:
      - courses: value
      TAKES:
      - dictionary: data
      - AS: key
        LITERAL: courses
  - get value:
      RESULT:
      - course id: value
      TAKES:
      - dictionary: course data
      - AS: key
        LITERAL: id
  - get item by id:
      RESULT:
      - prev course data: item
      TAKES:
      - list: courses
      - AS: id definition
        LITERAL: id
      - id value: course id
  - get changes:
      RESULT:
      - course changes: changes
      TAKES:
      - data: course data
      - previous data: prev course data
  - set value:
      TAKES:
      - dictionary: course changes
      - AS: key
        LITERAL: id
      - value: course id
  - make data:
      RESULT:
      - change list: data
      TAKES:
      - AS: template
        LITERAL:
        - courses:
          - param1
      - param1: course changes
      - AS: param2
        LITERAL: null
      - AS: param3
        LITERAL: null
  - get value:
      RESULT:
      - last change number: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - data change history
        - last change number
  - get value:
      RESULT:
      - is networking: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - settings
        - is networking
  - make data:
      RESULT:
      - parameters: data
      TAKES:
      - AS: template
        LITERAL:
          change list: param2
          context: param1
          last change number: param3
      - param1: context
      - param2: change list
      - param3: last change number
  - do alternatively:
      RESULT:
      - results
      TAKES:
      - context: parameters
      - alternative_value: is networking
      - AS: alternative_actions
        LITERAL:
        - ACTION: sync course tracker changes with server
          VALUE: true
  - get value:
      RESULT:
      - change list: value
      TAKES:
      - dictionary: results
      - AS: key
        LITERAL: change list
  - sync course tracker changes:
      TAKES:
      - context
      - change list
      - last change number
  TAKES:
  - view context: context
  - course data: data
save course data alternatives:
- ACTION: set new course id to course data
  VALUE: 0
save course data alternatives context definition:
  course data: null
  view context: null
save course tracker data:
  ALT STEPS:
  - get value:
      RESULT:
      - is networking: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - settings
        - is networking
  - make data:
      RESULT:
      - parameters: data
      TAKES:
      - AS: template
        LITERAL:
          context: param1
          course tracker data: param2
      - param1: context
      - param2: course tracker data
      - AS: param3
        LITERAL: null
  - do alternatively:
      TAKES:
      - context: parameters
      - alternative_value: is networking
      - AS: alternative_actions
        LITERAL:
        - ACTION: save course tracker data to server
          VALUE: true
        - ACTION: save course tracker data to file
          VALUE: false
        - ACTION: save course tracker data to file
          VALUE: null
  STEPS:
  - save course tracker data to file:
      TAKES:
      - context
      - course tracker data
  TAKES:
  - context
  - course tracker data
save course tracker data to file:
  STEPS:
  - get course tracker data storage path:
      RESULT:
      - storage path
      TAKES:
      - context
  - save yaml data to file:
      TAKES:
      - data: course tracker data
      - path: storage path
  - get course tracker data change history path:
      RESULT:
      - history path
      TAKES:
      - storage path
  - get value:
      RESULT:
      - data change history: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL: data change history
  - save yaml data to file:
      TAKES:
      - data: data change history
      - path: history path
  TAKES:
  - context
  - course tracker data
save course tracker settings:
  STEPS:
  - map path relative to application:
      RESULT:
      - path
      TAKES:
      - AS: path
        LITERAL: settings.yaml
  - get value:
      RESULT:
      - AS: settings
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL: settings
  - save yaml data to file:
      TAKES:
      - AS: data
        PARAM: settings
      - path
  TAKES:
  - context
save data by name:
  STEPS:
  - get yaml data path:
      RESULT:
      - data_path
      TAKES:
      - data_name
  - convert to yaml:
      RESULT:
      - AS: text
        WHAT: yaml
      TAKES:
      - AS: kpiece
        WHAT: data
  - write file text:
      TAKES:
      - AS: path
        WHAT: data_path
      - text
  TAKES:
  - data_name
  - data
save yaml data to file:
  STEPS:
  - convert to yaml:
      RESULT:
      - AS: text
        PARAM: yaml
      TAKES:
      - AS: kpiece
        PARAM: data
  - write file text:
      TAKES:
      - path
      - text
  TAKES:
  - path
  - data
schedule to string converter:
  USES:
  - convert schedule to string
  - convert string to schedule
  convert: convert schedule to string
  convert back: convert string to schedule
send course tracker request to server:
  RESULT:
  - response
  STEPS:
  - get value:
      RESULT:
      - server url: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL:
        - settings
        - server url
  - send yaml request to server:
      RESULT:
      - response
      TAKES:
      - server url
      - request
  TAKES:
  - context
  - request
send yaml request to server:
  RESULT:
  - response
  STEPS:
  - convert to yaml:
      RESULT:
      - request_yaml: yaml
      TAKES:
      - kpiece: request
  - EXEC: |
      from System import Uri
      from System.Net.Sockets import TcpClient
      from System.IO import StreamWriter
      from System.IO import StreamReader
      uri = Uri(server_url)
      with TcpClient(uri.Host, uri.Port if not uri.IsDefaultPort else 23423) as client:
        with client.GetStream() as stream:
          with StreamWriter(stream) as writer:
            with StreamReader(stream) as reader:
              writer.WriteLine(request_yaml)
              write_to_log(request_yaml)
              writer.WriteLine()
              writer.Flush()
              response_yaml = reader.ReadToEnd()
              write_to_log(response_yaml)
    RESULT:
    - response_yaml
    TAKES:
    - server_url: server url
    - request_yaml
  - load yaml in python:
      RESULT:
      - response: result
      TAKES:
      - yaml_content: response_yaml
  TAKES:
  - server url
  - request
set course tracker storage path:
  STEPS:
  - get value:
      RESULT:
      - AS: storage path
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL: storage path
  - set value:
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL:
        - view context
        - app context
        - settings
        - storage path
      - AS: value
        PARAM: storage path
  - get value:
      RESULT:
      - AS: app context
        PARAM: value
      TAKES:
      - AS: dictionary
        PARAM: context
      - AS: key
        LITERAL:
        - view context
        - app context
  - reload course tracker data:
      TAKES:
      - AS: context
        PARAM: app context
  - save course tracker settings:
      TAKES:
      - AS: context
        PARAM: app context
  TAKES:
  - context
set id for added data in change list:
  STEPS:
  - EXEC: |
      for changes in change_list:
        for course in changes.get('courses', []):
          if not course.get('id'):
            execute_knowledge_piece('set new course id if added', {'context': context, 'course data': course})
    TAKES:
    - context
    - change_list: change list
  TAKES:
  - context
  - change list
  USES:
  - course tracker context definition
  - set new course id if added
set item by id:
  STEPS:
  - EXEC: |
      item_indices = [index for index in range(len(list)) if list[index].get(id_name) == item.get(id_name)]
      if len(item_indices) > 0:
        for index in item_indices:
          list[index] = item
      else:
        list.append(item)
    TAKES:
    - AS: id_name
      PARAM: id definition
    - list
    - item
  TAKES:
  - list
  - item
  - id definition
set new course id if added:
  STEPS:
  - get value:
      RESULT:
      - course id: value
      TAKES:
      - dictionary: course data
      - AS: key
        LITERAL: id
  - make data:
      RESULT:
      - parameters: data
      TAKES:
      - AS: template
        LITERAL:
          context: param1
          course data: param2
      - param1: context
      - param2: course data
      - AS: param3
        LITERAL: null
  - do alternatively:
      TAKES:
      - context: parameters
      - alternative_value: course id
      - AS: alternative_actions
        LITERAL:
        - ACTION: set new course id to course data
          VALUE: 0
  TAKES:
  - context
  - course data
set new course id to course data:
  STEPS:
  - get value:
      RESULT:
      - course tracker data: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL: data
  - default if None:
      RESULT:
      - course tracker data: value
      TAKES:
      - value: course tracker data
      - AS: default value
        LITERAL: {}
  - get value:
      RESULT:
      - next course id: value
      TAKES:
      - dictionary: course tracker data
      - AS: key
        LITERAL: next course id
  - default if None:
      RESULT:
      - next course id: value
      TAKES:
      - value: next course id
      - AS: default value
        LITERAL: 1
  - set value:
      TAKES:
      - dictionary: course data
      - AS: key
        LITERAL: id
      - value: next course id
  - increment:
      RESULT:
      - next course id: value
      TAKES:
      - value: next course id
  - set value:
      TAKES:
      - dictionary: course tracker data
      - AS: key
        LITERAL: next course id
      - value: next course id
  TAKES:
  - context
  - course data
set value:
  STEPS:
  - EXEC: |
      if type(key) is not list:
        key = [key]
      if len(key) > 0:
        d = dictionary
        for key_part in key[0:-1]:
          d = d[key_part]
        d[key[-1]] = value
  TAKES:
  - dictionary
  - key
  - value
show open file dialog:
  RESULT:
  - path
  - is ok
  STEPS:
  - EXEC: |
      import clr
      clr.AddReferenceByPartialName('PresentationFramework')
      from System.IO import Path
      from Microsoft.Win32 import OpenFileDialog

      dir = Path.GetDirectoryName(initial_path)
      filename = Path.GetFileName(initial_path)

      dlg = OpenFileDialog()
      dlg.DefaultExt = '.*'
      dlg.Filter = 'All files (*.*)|*.*'
      dlg.InitialDirectory = dir
      dlg.FileName = filename

      result = dlg.ShowDialog()

      if result:
        path = dlg.FileName
        is_ok = True
      else:
        path = initial_path
        is_ok = False
    RESULT:
    - path
    - AS: is ok
      PARAM: is_ok
    TAKES:
    - AS: initial_path
      PARAM: initial path
  TAKES:
  - initial path
start from .net host:
  STEPS:
  - load .net host settings:
      RESULT:
      - settings
  - get value:
      RESULT:
      - value
      TAKES:
      - AS: dictionary
        WHAT: settings
      - AS: key
        WHAT: ProjectName
  - start project from .net host:
      TAKES:
      - AS: project name
        WHAT: value
start project from .net host:
  STEPS:
  - do alternatively:
      TAKES:
      - AS: context
        WHAT: None
      - AS: alternative_value
        WHAT: project name
      - AS: alternative_actions
        WHAT: .net project start alternatives
  TAKES:
  - project name
start server:
  RESULT:
  - server
  STEPS:
  - EXEC: |
      def dummy(context, handler, port):
        from System.Net.Sockets import TcpListener
        from System.Threading import Thread
        from System.Threading import ThreadStart
        from System.Threading import ParameterizedThreadStart
        from System.IO import StreamReader
        from System.IO import StreamWriter

        def connection_thread_func(client):
          try:
            with client.GetStream() as stream:
              with StreamReader(stream) as reader:
                with StreamWriter(stream) as writer:
                  while True:
                    request_lines = []
                    is_header_passed = False
                    while True:
                      line = reader.ReadLine()
                      if line is None:
                        break
                      write_to_log(line)
                      if len(request_lines) == 0 and line == '':
                        continue
                      # HACK
                      if line == '':
                        break
                      if not is_header_passed:
                        if line == '':
                          is_header_passed = True
                      else:
                        if line == '':
                          break
                      request_lines.append(line)
                    request = '\r\n'.join(request_lines)
                    try:
                      response = execute_knowledge_piece(handler, {'context': context, 'request text': request})['response text']
                    except:
                      writer.Write('error')
                      writer.Flush()
                      raise
                    else:
                      writer.Write(response)
                      writer.Flush()
                      break
          except:
            import traceback
            write_to_log(to_html(traceback.format_exc()))

        def server_thread_func():
          try:
            listener = TcpListener(port)
            listener.Start()
            try:
              while True:
                client = listener.AcceptTcpClient()
                connection_thread = Thread(ParameterizedThreadStart(connection_thread_func), Name = 'connection', IsBackground = True)
                connection_thread.Start(client)
            finally:
              listener.Stop()
          except:
            import traceback
            write_to_log(to_html(traceback.format_exc()))

        server_thread = Thread(ThreadStart(server_thread_func), Name = 'server', IsBackground = True)
        server_thread.Start()
        return server_thread
      server = dummy(context, handler, port)
  TAKES:
  - context
  - handler
  - port
sync course tracker changes:
  RESULT:
  - change list
  - last change number
  STEPS:
  - set id for added data in change list:
      TAKES:
      - context
      - change list
  - get value:
      RESULT:
      - data: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL: data
  - apply change list:
      RESULT:
      - data
      TAKES:
      - data
      - change list
  - set value:
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL: data
      - value: data
  - get value:
      RESULT:
      - history: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - data change history
        - history
  - append items to list:
      TAKES:
      - list: history
      - items: change list
  - get value:
      RESULT:
      - last change number: value
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - data change history
        - last change number
  - get length:
      RESULT:
      - number of changes: length
      TAKES:
      - collection: change list
  - add:
      RESULT:
      - last change number: result
      TAKES:
      - value1: last change number
      - value2: number of changes
  - set value:
      TAKES:
      - dictionary: context
      - AS: key
        LITERAL:
        - data change history
        - last change number
      - value: last change number
  - save course tracker data:
      TAKES:
      - context
      - course tracker data: data
  - get change list:
      RESULT:
      - change list
      TAKES:
      - data
      - history
      - start: client last change number
      - end: last change number
  TAKES:
  - context
  - change list
  - client last change number: last change number
  USES:
  - course tracker context definition
sync course tracker changes with server:
  RESULT:
  - change list
  - last change number
  STEPS:
  - make data:
      RESULT:
      - request: data
      TAKES:
      - AS: template
        LITERAL:
          command: sync changes
          data:
            change list: param1
            last change number: param2
      - param1: change list
      - param2: last change number
      - AS: param3
        LITERAL: null
  - send course tracker request to server:
      RESULT:
      - response
      TAKES:
      - context
      - request
  - get value:
      RESULT:
      - change list: value
      TAKES:
      - dictionary: response
      - AS: key
        LITERAL: change list
  - get value:
      RESULT:
      - last change number: value
      TAKES:
      - dictionary: response
      - AS: key
        LITERAL: last change number
  TAKES:
  - context
  - change list
  - last change number
update course tracker course: null
view definition:
  kinds:
  - list
  - column
  - command
  - dock panel
  - wrap panel
  - horizontal grid
  - vertical grid
  - data field
  - menu
  - menu item
weekday short names:
- mon
- tue
- wed
- thu
- fri
- sat
- sun
write .net exception to log:
  STEPS:
  - EXEC: |
      write_to_log('<div><pre>%s</pre></div>' % html_encode(exception.ToString()))
      data = dict(exception.Data)
      write_to_log('<div><pre>%s</pre></div>' % html_encode(to_yaml(data)))
      #info = dir(exception.Data['PythonExceptionInfo'].Value)
      #write_to_log('<div><pre>%s</pre></div>' % html_encode(info))
  TAKES:
  - exception
write file text:
  STEPS:
  - EXEC: |
      from System.IO import Directory
      from System.IO import File
      from System.IO import Path
      dir = Path.GetDirectoryName(path)
      if not Directory.Exists(dir):
        Directory.CreateDirectory(dir)
      File.WriteAllText(path, text)
  TAKES:
  - path
  - text
write python exception to log:
  STEPS:
  - EXEC: |
      import traceback
      write_to_log('<div><pre>%s</pre></div>' % html_encode(traceback.format_exc()))
write to log:
  STEPS:
  - convert to yaml:
      RESULT:
      - yaml
      TAKES:
      - kpiece: data
  - EXEC: write_to_log(to_html(yaml))
  TAKES:
  - data
